home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-04-03 | 13.2 KB | 428 lines | [TEXT/MPS ] |
- //----------------------------------------------------------------------------------------
- // UErrorMgr.cp
- // Copyright © 1984-96 by Apple Computer, Inc. All rights reserved.
- //----------------------------------------------------------------------------------------
-
- //----------------------------------------------------------------------------------------
- // Theory: courtesy of Larry Goldman
- //
- // Using Error Message Tables
- //
- // Part of MacApp's powerful failure handling facilities is the ability to give the user
- // useful alert messages. The MacApp Cookbook already explains how to use the common
- // features of the error reporting mechanism. But the messageLookup option is not
- // explained.Actually, this facility is not difficult to use, and allows the program to
- // report application-specific errors in the same way as other machine errors.
- //
- // Background
- //
- // To use the failure handling mechanism for custom errors, an exception handler must
- // first be posted using the CatchFailures routine.If an error is encountered, the Failure
- // routine must be called with application-defined parameter values. If, instead, the
- // routine completes without error, the exception handler is popped off the exception
- // handler stack with the Success routine.
- //
- // The Failure routine takes two parameters: error, which is an INTEGER, and message,
- // which is a LONGINT. During exception processing, these parameters are passed eventually
- // to the ErrorAlert global routine. ErrorAlert decodes the message parameter to determine
- // what alert to display and what subsititions need to be made.
- //
- // If the high word of the message is either messageLookup or messageAlternateRecovery, and if the
- // message table lookup is successful, then ErrorAlert will display MacApp's generic error
- // alert, which displays the alert message in the form: Could not ^2, because ^0. ^1.
- //
- // The first substitution (^2) is called the "operation", oropString, the ^0 substitution
- // is called the "reason", or errString, and the ^1 substitution is called the recovery.
- //
- // Message Tables
- //
- // If the high word of the message is messageLookup or messageAlternateRecovery, ErrorAlert uses message
- // tables, located in the application's resource file, to determine the text of each of
- // the substitutions that will be displayed in the "generic" error alert.
- //
- // In both message cases, the low word of the message parameter is used to lookup the
- // opString, and the error parameter is used to lookup the errString. In the messageLookup
- // case, the error parameter is also used to determine the recovery. In the messageAlternateRecovery
- // case, the low word of the message parameter is used, instead, to determine recovery.
- //
- // ErrorAlert uses the global procedure LookupErrString to search message tables for the
- // text to substitute into the alert. LookupErrString first scans the MacApp message
- // tables, and if the message cannot be found, then application message tables are
- // scanned.
- //
- // Each message table has two parts in the resource file: an 'errs' resource which
- // contains the list of index ranges, and a 'STR#' list of messages that correspond to
- // each index range. This allows a single text message to cover a range of indexes, if
- // necessary.
- //
- // The index ranges are specified as an array of three integers: the first is the lowest
- // index to which the message applies, the next is the highest index to which the message
- // applies, and the last integer is the item in the CString list that contains the text of
- // the message to use. The first line of the index table, the one that begins with the
- // whichList constant,is used to specify the resource number of the STR# list to use for
- // this index table.
- //
- // Here is a sample message index table:
- //
- // resource 'errs' (1130, purgeable) { // application's operation table
- // { whichList, 0, 2103; // indicates that STR# 2103 will be used by this table
- // -26001, -26001, 1; // lowIndex, hiIndex, STR# item
- // -26002, -26002, 2;
- // -26003, -26003, 3
- // }
- // };
- //
- // Here is the corresponding STR# list:
- //
- // resource 'STR#' (2103, purgeable) { // application's operation strings
- // {
- // "use the saved configuration";
- // "add another character";
- // "accept the number"
- // }
- // };
- //
- // The application's resource file normally contains three sets of message tables: one
- // each for the operation, error and recovery strings. The resource numbers for the tables
- // are fixed: the application's operation table must have number 1130, the reason table
- // must have number 1128 and the recovery table must have number 1129.
- //
- // Recipe
- //
- // To use application message tables:
- //
- // 1) Define constants for the application's message and error:
- // CONST
- // msgCantAcceptChar = messageLookup + -26002;
- // {Note: Applications should use errors between -26000 and -29999.}
- // errCantAddChar= -26002;
- // Adding the predefined constant messageLookup to the message signals ErrorAlert to use the message
- // tables to find text to substitute into the generic alert.
- //
- // 2) Define the message tables in the application's resource file:
- // First, the operation table and its STR# list:
- // resource 'errs' (1130, purgeable) { // application's operation table
- // { whichList, 0, 2103, // indicates that STR# 2103 will be used by this table
- // -26002, -26002, 1
- // }
- // };
- // resource 'STR#' (2103, purgeable) { // application's operation strings
- // {
- // "add another character"
- // }
- // };
- //
- // Next, the errortable and its STR# list:
- // resource 'errs' (1128, purgeable) { // application's reason table
- // { whichList, 0, 2101;
- // -26002, -26002, 1
- // }
- // };
- // resource 'STR#' (2101, purgeable) { // application's error reason strings
- // {
- // "only ^3 characters are allowed"
- // }
- // };
- //
- // Note that ^3 can be used in any message CString. ErrorAlert always subsitutes the text
- // in the global variable gErrorParm3 for ^3, if present, in the message strings. Thus,
- // gErrorParm3 can be used by the application to further customize the alert according to
- // the context of the program at the time the error was encountered.
- //
- // Finally, the recovery table and its STR# list:
- // resource 'errs' (1129, purgeable) { // application's recovery table
- // { whichList, 0, 2102;
- // -26002, -26002, 1
- // }
- // };
- // resource 'STR#' (2102, purgeable) { // application's error recovery strings
- // {
- // "Delete some characters before adding new ones."
- // }
- // };
- //
- // 3) If, in the course of executing the program, an error is encountered, call Failure:
- // PROCEDURE TMyEditText.AddCharacter(ch: CHAR);
- // VAR
- // fi : FailInfo;
- // PROCEDURE HandleFailure(error: INTEGER; message: LONGINT);
- // BEGIN
- // IF error = errCantAddChar THEN {if the error is recognized, fill in the param}
- // NumToString(fMaxCharsAllowed, gErrorParm3);
- // {Note: the error will be propagated automatically to exception handlers already
- // posted on the exception handler stack. Eventually, ErrorAlert will be called to
- // display the message with the text located in the application's message tables.}
- // END;
- // BEGIN
- // CatchFailures(fi, HandleFailure);{Setup an exception handler}
- // IF NumOfChars = fMaxCharsAllowed THEN {Assume fMaxCharsAllowed = 15, for
- // example}
- // Failure(errCantAddChar, msgCantAcceptChar){Can't add a char, so signal failure.}
- // ELSE
- // INHERITED AddCharacter(ch);{Can accept char}
- // Success(fi);{Done with exception handler.}
- // END;
- //
- // 4) If the character could not be added, then the generic alert will appear on the
- // screen with the text:
- // Could not add another character, because only 15 are allowed. Delete some characters
- // before adding new ones.
- //----------------------------------------------------------------------------------------
-
-
- #ifndef __UERRORMGR__
- #include "UErrorMgr.h"
- #endif
-
- // MacApp
-
- #ifndef __UAPPLEEVENTS__
- #include "UAppleEvents.h"
- #endif
-
- // #ifndef __UAPPLICATION__
- // #include "UApplication.h"
- // #endif
-
- #ifndef __UDEBUG__
- #include "UDebug.h"
- #endif
-
- // #ifndef __UDISPATCHER__
- // #include "UDispatcher.h"
- // #endif
-
- #ifndef __UFAILURE__
- #include "UFailure.h"
- #endif
-
- #ifndef __UMACAPPGLOBALS__
- #include "UMacAppGlobals.h"
- #endif
-
- #ifndef __UMACAPPUTILITIES__
- #include "UMacAppUtilities.h"
- #endif
-
- #ifndef __UMEMORY__
- #include "UMemory.h"
- #endif
-
- #ifndef __UMENUMGR__
- #include "UMenuMgr.h"
- #endif
-
- #ifndef __USCRIPTING__
- #include "UScripting.h"
- #endif
-
- // Toolbox
-
- #ifndef __APPLEEVENTS__
- #include <AppleEvents.h>
- #endif
-
- #ifndef __MEMORY__
- #include <Memory.h>
- #endif
-
- #ifndef __PACKAGES__
- #include <Packages.h>
- #endif
-
- #ifndef __RESOURCES__
- #include <Resources.h>
- #endif
-
- #ifndef __TOOLUTILS__
- #include <ToolUtils.h>
- #endif
-
- // ANSI
-
- #ifndef __STDIO__
- #include <stdio.h>
- #endif
-
-
- //----------------------------------------------------------------------------------------
- //CStr255 gErrorParm3;
- ProcPtr gMacAppAlertFilter;
- Boolean gInFilter;
- Boolean gInhibitNestedHandling; // Allow nested handling
-
- //========================================================================================
- // GLOBAL Procedures
- //========================================================================================
-
- //----------------------------------------------------------------------------------------
- // ErrorAlert:
- //----------------------------------------------------------------------------------------
- #pragma segment MAError
-
- void ErrorAlert(OSErr err,
- long message)
- {
- union Converter
- {
- struct
- {
- short hiWd, loWd;
- } shortVal;
- long message;
- };
-
-
- const long kMsgCmdErr = messageCommandError / 0x10000;
- const long kMsgAlert = messageAlert / 0x10000;
- const long kMsgLookup = messageLookup / 0x10000;
- const long kMsgAltRecov = messageAlternateRecovery / 0x10000;
-
- CStr255 opString;
- CStr255 errStr;
- CStr255 recovery;
- Converter c;
- ResNumber alertID = phGenError; // the default alert
- Boolean genericAlert = TRUE;
- OSErr recovErr;
-
- c.message = message;
-
- switch (c.shortVal.hiWd)
- {
- case kMsgCmdErr:
- CommandNumber theCommand = c.shortVal.loWd;
- if (theCommand != cNoCommand)
- {
- alertID = phCommandError;
- CommandToName(theCommand, opString);
- }
- break;
-
- case kMsgAlert:
- alertID = c.shortVal.loWd;
- genericAlert = FALSE;
- break;
-
- case kMsgLookup:
- case kMsgAltRecov:
- LookupErrString(c.shortVal.loWd, errOperationsID, opString);
- break;
-
- default:
- GetIndString(opString, c.shortVal.hiWd, c.shortVal.loWd);
- break;
- }
-
- if (genericAlert)
- {
- LookupErrString(err, errReasonID, errStr);
-
- if (c.shortVal.hiWd == kMsgAltRecov)
- recovErr = c.shortVal.loWd;
- else
- recovErr = err;
-
- LookupErrString(recovErr, errRecoveryID, recovery);
-
- ParamText((ConstStr255Param)&errStr, (ConstStr255Param)&recovery,
- (ConstStr255Param)&opString, (ConstStr255Param)&gErrorParm3);
-
- if (opString.IsEmpty())
- alertID = phUnknownErr;
- }
-
- StdAlert(alertID);
- gInhibitNestedHandling = FALSE; // Used suppress nested event handling
-
- if (genericAlert)
- ResetAlertStage();
- } // ErrorAlert
-
- //----------------------------------------------------------------------------------------
- // StdAlert:
- //----------------------------------------------------------------------------------------
- #pragma segment MAGlobalsRes
- // Don't require a segment load for this
-
- void StdAlert(ResNumber alertID)
- {
- MacAppAlert(alertID, NULL);
- } // StdAlert
-
- //----------------------------------------------------------------------------------------
- // MacAppAlert:
- //----------------------------------------------------------------------------------------
- // Don't require a segment load for this
- #pragma segment MAGlobalsRes
-
- short MacAppAlert(ResNumber alertID,
- ProcPtr filterProc)
- {
- AlertTHndl alrtTemplate;
- SignedByte savedState;
- short alertReturn;
-
- gRsrcCheck = 0; // force immediate check.
-
- // don't provide an idle proc, since we don't want events right now
- MAInteractWithUserNoIdleProc();
-
- short oldResFile = MAUseResFile(gApplicationRefNum);
-
- SetCursor(&(qd.arrow));
-
- // preflight the ALRT
- alrtTemplate = (AlertTHndl)GetResource('ALRT', alertID);
- if (!alrtTemplate)
- {
- #if qDebug
- CStr255 theString;
- ConcatNumber("Unable to find or load 'ALRT' resource ", alertID, theString);
- ProgramBreak(theString);
- #endif
-
- SysBeep(2); // At least give some indication
- MAUseResFile(oldResFile);
- return cancel; // safe result
- }
-
- // preflight the DITL
- if (!GetResource('DITL', (*alrtTemplate)->itemsID))
- {
- #if qDebug
- CStr255 theString;
- ConcatNumber("Unable to find or load 'DITL' resource ", alertID, theString);
- ProgramBreak(theString);
- #endif
-
- SysBeep(2); // At least give some indication
- MAUseResFile(oldResFile);
- return cancel; // safe result
- }
-
- // Success at last!
- savedState = LockHandleHigh((Handle)alrtTemplate);
-
- // Assume that the bit is set in the ALRT template in order to do the centering.
-
- PullApplicationToFront();
-
- ProcPtr aProcPtr = (filterProc == NULL) ? gMacAppAlertFilter : filterProc;
- ModalFilterUPP theFilter = NewModalFilterProc(aProcPtr);
-
- alertReturn = Alert(alertID, theFilter);
-
- theFilter = (ModalFilterUPP)DisposeIfRoutineDescriptor((UniversalProcPtr)theFilter);
-
- // restore the state of the DITL's handle
- HSetState((Handle)alrtTemplate, savedState);
- MAUseResFile(oldResFile);
- return alertReturn;
- } // MacAppAlert
-
- //----------------------------------------------------------------------------------------
- // End of UErrorMgr.cp
-
- #pragma segment Inline
-